/*
 * Copyright (C) 2009, 2010, B3log Team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Description: Editor.
 * Version: 1.0.0.8, Aug 3, 2010
 * Author: Liyuan Li
 * Author: Liang Ding
 */
var editor1;
$(function() {
    editor1 = $('#editorDiv').editor();
    editor1.editor('init');
    editor1.editor('openFile');
});

(function($) {
    $.fn.extend({
        editor: {
            version: "0.0.4.3",
            author: "lly219@gmail.com"
        }
    });

    var dpuuid = new Date().getTime();
    var PROP_NAME = 'editor';

    // editor value.
    var Editor = function(){
        // If these attributes undefined in new, should be use those default value.
        this._defaults = {
            "styleClass": {
                "borderClass": "editor-border",
                "lineClass": "editor-line",
                "methodNameClass": "editor-methodName",
                "methodParameterTypesClass": "editor-methodParameterTypes",
                "methodReturnTypeClass": "editor-methodReturnType",
                "clearClass": "editor-clear"
            },
            codeCompleteId: 'editorCodeComplete',
            sources: [],
            styles: [[{}]],
            fileName: "", // TODO: useful?
            cursor: {
                col: 0,
                row: 0
            },
            CodeCompletePosition: {
                top: 0,
                left: 0
            },

            LINE_SEPARATOR: "enter-0=*",
            LINE_SEPARATOR_REX: /enter-0=\*/g,
            SPACE: "space-0=*",
            SPACE_REX: /space-0=\*/g,
            editorId: "editor",
            lineNumId: "lineNumDiv",
            
            socket: undefined
        },

        this._settingsDataFormat = {
        }
    };

    $.extend(Editor.prototype, {
        _attachEditor: function(target, settings) {
            if (!target.id) {
                this.uuid++;
                target.id = 'dp' + this.uuid;
            }
            var inst = this._newInst($(target));
            inst.settings = $.extend({}, settings || {});
            $.data(target, PROP_NAME, inst);
        },

        /* Create a new instance object. */
        _newInst: function(target) {
            // escape jQuery meta chars
            var id = target[0].id.replace(/([^A-Za-z0-9_])/g, '\\\\$1');
            return {
                id: id
            };
        },

        _getInst: function(target) {
            try {
                return $.data(target, PROP_NAME);
            } catch (err) {
                throw 'Missing instance data for this editor';
            }
        },

        _destroyEditor: function() {
        },

        _initEditor: function(target) {
            var inst = this._getInst(target);
            var settings = inst.settings,
            id = inst.id;
            var styleClass = this._getDefaults(settings, "styleClass"),
            codeCompleteId = this._getDefaults(settings, "codeCompleteId");

            // base framework
            target.innerHTML = "<div id='" + id + "Line' class='left " + styleClass.lineClass+ "'></div>"
            + "<div id='" + id + "Editor' tabIndex='0'></div>"
            + "<div id='" + id + "Tip' class='" + styleClass.borderClass+ "'></div>";


            var editorSocket = new WebSocket(document.location.toString().
                replace("http:", "ws:").replace("index", "editor"));

            this._defaults.socket = editorSocket;
            
            editorSocket.onmessage = function(event) {
                var resultJSONObject = JSON.parse(event.data);

                switch (resultJSONObject.sc) {
                    case "EDITOR_CONNECTED":
                        $("#" + id + "Tip").html(resultJSONObject.msgs[0]);
                        break;
                    case "JAVA_CODE_ASSISTANT_GET_METHODS_SUCC":
                        $.editor._codeComplete(resultJSONObject.javaClassMethods, codeCompleteId, styleClass);
                        break;
                    default:
                        alert("default case: " + resultJSONObject.sc);
                        break;
                }
            };

            editorSocket.onclose = function() {
                $("#" + id + "Tip").html("<span style='color: red;'>Editor connection closed!</span>");
            };

            editorSocket.onopen = function() {
                var data = {
                    "webSocketGreeting": "Hello server, I am editor."
                };

                this.send(JSON.stringify(data));

            //            window.setInterval(function() {
            //                keepAlive(editor.socket, data);
            //            }, 10000);
            };

            $("#" + id + "Editor").click(function(){
                $("#" + codeCompleteId).hide();
                $(this).focus();
            }).bind("keyup", {
                target: target
            }, $.editor._keyup).bind('keypress', {
                target: target
            }, $.editor._keypress).keydown(function(event) {
                if (event.keyCode == 38 || event.keyCode == 40) {
                    event.preventDefault();
                }
                if (event.keyCode == 8) {
                    return false; 
                }
            });
        },

        _openFileEditor: function(target) {
            var filePath = projectSpaceTree.getSelectedFilePath();
            //var filePath = "nide-test/src/main/java/org/b3log/App.java";
            if ("" !== filePath) {
                if (!isDirectory(filePath)) {
                    var requestJSONObject = {
                        "filePath": filePath
                    };

                    jsonRpc.projectService.getFileAsMatrix(function(result, error) {
                        var inst = $.editor._getInst(target);
                        var settings = inst.settings;
                        settings.fileName = result.fileName;
                        settings.sources = result.fileContentMatrix;
                        settings.styles = result.fileStyleMatrix;
                        $.editor._buildFile(target);
                    },
                    requestJSONObject);
                }
            }
        },

        _saveFileEditor: function(target) {
            //var filePath = projectSpaceTree.getSelectedFilePath();
            var filePath = "nide-test/src/main/java/org/b3log/App.java";
            var requestJSONObject = {
                "filePath": filePath,
                "fileContent": $.editor._getContent($.editor._getInst(target).settings.sources)
            };

            jsonRpc.projectService.saveFile(function(result, error) {
                // TODO: save file callback
                },
                requestJSONObject);
        },

        _goPositionEditor: function(target) {
            var inst = $.editor._getInst(target),
            id = inst.id;
            var settings = inst.settings;
            var sources = settings.sources,
            cursor = $.editor._getDefaults(settings, "cursor");

            $("#" + id + "Editor span").get($.editor._getItemIndex(sources, cursor.row, cursor.col)).innerHTML
            = $.editor._getItemContent(sources, cursor.row, cursor.col);
        
            cursor.row = parseInt($('#goRow').val() - 1);
            cursor.col = parseInt($('#goColumn').val() - 1);

            $("#" + id + "Editor span").get($.editor._getItemIndex(sources, cursor.row, cursor.col)).innerHTML
            = "|" + $.editor._getItemContent(sources, cursor.row, cursor.col);

            $("#" + id + "Editor").focus();
        },

        _buildFile: function(target) {
            var inst = $.editor._getInst(target);
            var settings = inst.settings,
            id = inst.id;
            var sources = settings.sources,
            styles = settings.styles,
            codeArea = document.getElementById(id + "Editor"),
            editorHTML = "",
            lineNumHTML = "";
            for (var i = 0; i < sources.length; i++) {
                for (var j = 0; j < sources[i].length; j++) {
                    var ch = sources[i][j];
                    if (ch === " ") {
                        ch = "&nbsp;";
                    }
                    if (i == 0 && j == 0) {
                        ch = "|" + ch;
                    }
                    editorHTML += "<span style='color:" + styles[i][j].fileStyleCharColor
                    + "; font-weight:" + styles[i][j].fileStyleCharFontWeight 
                    + "; font-style:" + styles[i][j].fileStyleCharFontStyle +"'>"
                    + ch + "</span>";
                }
                editorHTML += "<span></span><br/>";
                lineNumHTML += "<div>" + (i + 1) + "</div>";
            }

            codeArea.innerHTML = editorHTML;

            document.getElementById(id + "Line").innerHTML = lineNumHTML;
        },
        
        _getDefaults: function(settings, key) {
            if (key == 'styleClass') {
                settings[key] = $.extend({}, $.editor._defaults[key]);
                if (settings.theme == 'default' || settings.theme == undefined) {
                    return $.editor._defaults[key];
                }
                for (var styleClass in $.editor._defaults[key]) {
                    settings[key][styleClass] = settings.theme + "-" + $.editor._defaults[key][styleClass];
                }
            } else {
                if (settings[key] == null || settings[key] == undefined) {
                    return $.editor._defaults[key];
                }
            }
            return settings[key];
        },

        _setCodeCompletePosition: function(it) {
            $.editor._defaults.CodeCompletePosition = {
                left: $(it).position().left + 5,
                top:  $(it).position().top + 10
            }
        },

        _getContent: function(sources) {
            var content = "";
            for (var i = 0; i < sources.length; i++) {
                content += sources[i].join("") + "\r\n";
            }
            return content;
        },

        _keypress: function(event) {
            var target = event.data.target;
            var inst = $.editor._getInst(target);
            var settings = inst.settings;
            var sources = settings.sources,
            cursor = $.editor._getDefaults(settings, "cursor"),
            socket = $.editor._getDefaults(settings, "socket"),
            currentSpan = $("#" + inst.id + "Editor span").get($.editor._getItemIndex(sources, cursor.row, cursor.col));
            
            if (event.charCode !== 13) {
                if (event.charCode === 46) { // point
                    var requestJSONObject = {
                        'webSocketJavaCodeAssistantGetMethods': "TODO",
                        "javaSourceCode": $.editor._getContent(sources),
                        "fileLineNum": cursor.row + 1,
                        "fileColumnNum": cursor.col + 1
                    };
                    socket.send(JSON.stringify(requestJSONObject));
                    $.editor._setCodeCompletePosition(currentSpan);
                }
                var ch = String.fromCharCode(event.charCode);
                sources[cursor.row].splice(cursor.col, 0, ch);

                if (event.charCode == 32) {
                    ch = '&nbsp;';
                }

                $(currentSpan).before("<span>" + ch + "</span>");
            
                cursor.col++;
            }
        },

        _keyup: function(event) {
            var target = event.data.target;
            var inst = $.editor._getInst(target),
            id = inst.id;
            var settings = inst.settings;
            var sources = settings.sources,
            cursor = $.editor._getDefaults(settings, "cursor");
            var tempLine = cursor.row,
            tempColumn = cursor.col,
            currentSpan  = $($("#" + id + "Editor span").get($.editor._getItemIndex(sources, cursor.row, cursor.col)));

            switch (event.keyCode) {
                case 38: // up
                    if (0 < cursor.row) {
                        cursor.row--;
                        var upLineLength =  sources[cursor.row].length;
                        if (cursor.col > upLineLength) {
                            cursor.col = upLineLength;
                        }
                    }
                    break;
                case 40: // down
                    if (cursor.row < sources.length - 1) {
                        cursor.row++;
                        var nextLineLength = sources[cursor.row].length;

                        if (cursor.col > nextLineLength) {
                            cursor.col = nextLineLength;
                        }
                    }
                    break;
                case 37: // left
                    if (0 < cursor.col) {
                        cursor.col--;
                    } else {
                        if (cursor.row > 0) {
                            cursor.row--;
                            cursor.col = sources[cursor.row].length;
                        }
                    }
                    break;
                case 39: // right
                    if (cursor.col < sources[cursor.row].length) {
                        cursor.col++;
                    } else {
                        if (cursor.row < sources.length - 1) {
                            cursor.row++;
                            cursor.col = 0;
                        }
                    }
                    break;
                case 13: // enter
                    if (2000 == cursor.row + 1) {
                        alert("out of line!");
                    } else {
                        currentSpan.after($.editor._getNewLineHTML(id, sources, cursor.row, cursor.col));
                        cursor.row ++;
                        cursor.col = 0;
                    }
                    break;
                case 8: // backspaces
                    if (cursor.col == 0) {
                        if (cursor.row > 0) {
                            cursor.row--;
                            cursor.col = sources[cursor.row].length;
                            sources[cursor.row] = sources[cursor.row].concat(sources[cursor.row + 1]);
                            sources.splice(cursor.row + 1, 1);
                            currentSpan.remove();
                            $($("#" + id + "Editor br").get(cursor.row)).remove();
                            $($("#" + id + "Line div").get(sources.length)).remove();
                        }
                    } else {
                        cursor.col--;
                        currentSpan.remove();
                        sources[cursor.row].splice(cursor.col, 1);
                    }
                    break;
                case 46: // delete
                    if (cursor.col == sources[cursor.row].length) {
                        if (cursor.row < sources.length) {
                            sources[cursor.row] = sources[cursor.row].concat(sources[cursor.row + 1]);
                            sources.splice(cursor.row + 1, 1);
                            currentSpan.remove();
                            $($("#" + id + "Editor br").get(cursor.row)).remove();
                            $($("#" + id + "Line div").get(sources.length)).remove();
                        }
                    } else {
                        currentSpan.remove();
                        sources[cursor.row].splice(cursor.col, 1);
                    }
                    break;
                default:
                    break;
            }
            
            $("#" + id + "Editor span").get($.editor._getItemIndex(sources, cursor.row, cursor.col)).innerHTML
            = "|" + $.editor._getItemContent(sources, cursor.row, cursor.col);

            if ((tempLine != cursor.row || tempColumn != cursor.col) && event.keyCode != 8) {
                $("#" + id + "Editor span").get($.editor._getItemIndex(sources, tempLine, tempColumn)).innerHTML
                = $.editor._getItemContent(sources, tempLine, tempColumn);
            }
            
            output.clear();
            output.appendHtml("lineSource:" + sources[cursor.row]);
            output.appendHtml("line: " + (cursor.row + 1)
                + "; column: " + (cursor.col + 1));
        },

        _getItemIndex: function(sources, row, col) {
            var num = 0;
            for (var i = 0; i < row; i++){
                num += sources[i].length + 1;
            }
            return num + col;
        },


        _getItemContent: function(sources, row, col) {
            var item = sources[row][col];
            if (item == " "){
                item = "&nbsp;";
            }
            if (item == undefined){
                item = "";
            }
            return item;
        },

        _getNewLineHTML: function(id, sources, row, col) {
            sources.splice(row + 1, 0, sources[row].slice(col));
            sources[row].splice(col, sources[row].length - col);

            $($("#" + id + "Line div").last()).after("<div>" + sources.length + "</div>");
            return "<br/><span></span>";
        },

        _codeComplete: function(tipMethods, codeCompleteId, styleClass) {
            var tipHTML = "";

            for(var i = 0; i < tipMethods.length; i++){
                var methodParameterTypes = tipMethods[i].methodParameterTypes;

                tipHTML +=  "<div><span class='" + styleClass.methodNameClass + "'>"
                + tipMethods[i].methodName
                + "</span><span class='" + styleClass.methodParameterTypesClass + "'>("
                + $.editor._getMethodParameterTypesString(methodParameterTypes)
                + ")</span><span class='" + styleClass.methodReturnTypeClass + "'>"
                + $.editor._getMethodReturnType(tipMethods[i].methodReturnType)
                + "</span></div><div class='" + styleClass.clearClass + "'></div>";
            }

            $("#" + codeCompleteId).show().css({
                "top": $.editor._defaults.CodeCompletePosition.top,
                "left": $.editor._defaults.CodeCompletePosition.left
            }).html(tipHTML);
        },

        _getMethodParameterTypesString: function(methodParameterTypes) {
            var methodParameterTypesString = '',
            methodParaLength = methodParameterTypes.length;
            
            for (var i = 0; i < methodParaLength; i++) {
                var methodParameterType = methodParameterTypes[i].split(".");
                methodParameterTypesString += methodParameterType[methodParameterType.length - 1];

                if (i < methodParaLength - 1) {
                    methodParameterTypesString += ", ";
                }
            }

            return methodParameterTypesString;
        },

        _getMethodReturnType: function(methodReturnType) {
            var ret = methodReturnType.split(".");
            
            return ret[ret.length - 1];
        }
    });

    $.fn.editor = function(options) {
        var otherArgs = Array.prototype.slice.call(arguments);
        return this.each(function() {
            typeof options == 'string' ?
            $.editor['_' + options + 'Editor'].apply($.editor, [this].concat(otherArgs)) :
            $.editor._attachEditor(this, options);
        });
    };

    $.editor = new Editor();

    // Add another global to avoid noConflict issues with inline event handlers
    window['DP_jQuery_' + dpuuid] = $;
})(jQuery);
